﻿using System;
using System.Collections.Generic;
using System.Security.Permissions;
using Moe.ECamel.Common.Contracts;
using Moe.ECamel.Common.Entities;
using Moe.ECamel.Common.Security;
using Moe.ECamel.Server.BL;

namespace Moe.ECamel.Server.Services
{
	public class ItemReservationService : ECamelService, IItemReservationService
	{
		public ItemReservationService()
			:base()
		{
		}

		public ItemReservationService(ECamelService caller)
			: base(caller)
		{
		}

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.InventoryRequestReserve)]
		public void AddItemReservation(int itemId, int customerId, DateTime createdOn)
		{
			Customer customer;
			using (var customerService = new CustomerService(this))
			{
				customer = customerService.GetCustomerById(customerId);
			}

			using (var bl = new ItemReservationBL(GetReadWriteDataContext()))
			{
				// Get last reservation for this item in the customer's library, if any:
				var lastItemReservation = bl.GetLastItemReservation(itemId, customer.LibraryId);
				// Create a new reservation for this item for this customer after the last reservation:
				bl.CreateItemReservation(itemId, customer.CustomerId, createdOn, lastItemReservation);
			}

			SubmitChanges();
		}

		public bool ReserveInventoryIfNeeded(Inventory inventory, DateTime reserveDate)
		{
			ItemReservation itemReservation;
			using (var bl = new ItemReservationBL(GetReadWriteDataContext()))
			{
				// Get first reservation for an item of the inventory's type and library (if any):
				itemReservation = bl.GetFirstItemReservation(inventory.ItemId, inventory.LibraryId);
				// If no reservation, abort:
				if (itemReservation == null)
				{
					inventory.IsAvailable = true;
					return false;					
				}
				
				// update reservation details:
				itemReservation.IsFirst = false;
				itemReservation.ReservedInventory = inventory;
				itemReservation.ReservedOn = reserveDate;
				itemReservation.ReservedUntil = reserveDate.AddDays(4);

				if (itemReservation.NextItemReservationId != null)
				{
					var nextReservation = bl.GetSuccessor(itemReservation);
					nextReservation.IsFirst = true;
					itemReservation.NextItemReservationId = null;
				}
			}
			var subject = "הפריט שביקשת לשכור זמין כעת";
			var body = "הפריט '" + itemReservation.Item.Title + "' שביקשת לשכור זמין כעת, ויישמר עבורך עד לתאריך " + itemReservation.ReservedUntil.Value.ToShortDateString() + ".";
			var mailMessage = new MailMessage(itemReservation.Customer, itemReservation.ReservedOn.Value, subject, body);
			using (var mailService = new MailMessageService(this))
			{
				mailService.SendMessage(mailMessage);
			}

			inventory.IsAvailable = false;
			return true;
		}

		public Inventory GetReservedInventory(int itemId, int customerId)
		{
			ItemReservation itemReservation;
			using (var bl = new ItemReservationBL(GetReadWriteDataContext()))
			{
				// Find an available reservation, if any:
				itemReservation = bl.GetAvailableItemReservation(itemId, customerId);
				if (itemReservation == null)
					return null;
				// Delete the reservation - it is no longer needed:
				bl.DeleteItemReservation(itemReservation);
			}

			// Get the reserved inventory:
			using (var inventoryService = new InventoryService(this))
			{
				return inventoryService.GetInventory(itemReservation.ReservedInventoryId.Value);
			}
		}

		public void UpdateItemReservationList(DateTime date)
		{
			List<MailMessage> messages = new List<MailMessage>();

			using (var bl = new ItemReservationBL(GetReadWriteDataContext()))
			{
				// Find reservations which are overdue:
				var overdueReservations = bl.GetOverdueReservations(date);

				foreach (var itemReservation in overdueReservations)
				{
					// Notify user it is no longer reserved:
					var subject = "הפריט שנשמר עבורך הועבר לבא בתור";
					var body = "הפריט '" + itemReservation.Item.Title + "' שביקשת נשמר עבורך עד כה, אך מכיוון שלא שכרת אותו הוא הועבר לבא בתור ברשימת ההמתנה.";
					messages.Add(new MailMessage(itemReservation.Customer, date, subject, body));

					// Delete reservation:
					bl.DeleteItemReservation(itemReservation);
					// Reserve inventory for next in waiting list, or release it:
					ReserveInventoryIfNeeded(itemReservation.ReservedInventory, date);
				}
			}

			using (var mailMessageService = new MailMessageService(this))
			{
				foreach (var message in messages)
					mailMessageService.SendMessage(message);
			}

			SubmitChanges();
		}
	}
}
